﻿namespace VirtualTemplates
{
    using System;
    using System.Collections;
    using System.Diagnostics.Contracts;
    using System.Web.Caching;
    using System.Web.Hosting;
    using System.Linq;
    using System.Collections.Generic;

    /// <summary>
    /// A virtual path provider that provides virtual templates for web applications.
    /// </summary>
    public class VirtualTemplateProvider : VirtualPathProvider
    {
        private readonly IVirtualTemplate virtualTemplate;

        /// <summary>
        /// Initializes a new instance of the <see cref="VirtualTemplateProvider"/> class.
        /// </summary>
        /// <param name="virtualTemplate">The virtual template.</param>
        public VirtualTemplateProvider(IVirtualTemplate virtualTemplate)
        {
            Contract.Requires(virtualTemplate != null);

            this.virtualTemplate = virtualTemplate;
        }

        /// <summary>
        /// Gets a value that indicates whether a file exists in the virtual file system.
        /// </summary>
        /// <param name="virtualPath">The path to the virtual file.</param>
        /// <returns>
        /// true if the file exists in the virtual file system; otherwise, false.
        /// </returns>
        public override bool FileExists(string virtualPath)
        {
            return base.FileExists(virtualPath) || this.virtualTemplate.FileExists(virtualPath);
        }

        /// <summary>
        /// Gets a virtual file from the virtual file system.
        /// </summary>
        /// <param name="virtualPath">The path to the virtual file.</param>
        /// <returns>
        /// A descendent of the <see cref="T:System.Web.Hosting.VirtualFile"/> class that represents a file in the virtual file system.
        /// </returns>
        public override VirtualFile GetFile(string virtualPath)
        {
            return base.FileExists(virtualPath) 
                ? base.GetFile(virtualPath) 
                : this.virtualTemplate.GetFile(virtualPath);
        }

        /// <summary>
        /// Gets a value that indicates whether a directory exists in the virtual file system.
        /// </summary>
        /// <param name="virtualDir">The path to the virtual directory.</param>
        /// <returns>
        /// true if the directory exists in the virtual file system; otherwise, false.
        /// </returns>
        public override bool DirectoryExists(string virtualDir)
        {
            return base.DirectoryExists(virtualDir) || this.virtualTemplate.DirectoryExists(virtualDir);
        }

        /// <summary>
        /// Gets a virtual directory from the virtual file system.
        /// </summary>
        /// <param name="virtualDir">The path to the virtual directory.</param>
        /// <returns>
        /// A descendent of the <see cref="T:System.Web.Hosting.VirtualDirectory"/> class that represents a directory in the virtual file system.
        /// </returns>
        public override VirtualDirectory GetDirectory(string virtualDir)
        {
            var baseDirectory = base.GetDirectory(virtualDir);

            return this.virtualTemplate.DirectoryExists(virtualDir) 
                ? new MergedVirtualDirectory(baseDirectory, this.virtualTemplate.GetDirectory(virtualDir)) 
                : baseDirectory;
        }

        /// <summary>
        /// Creates a cache dependency based on the specified virtual paths.
        /// </summary>
        /// <param name="virtualPath">The path to the primary virtual resource.</param>
        /// <param name="virtualPathDependencies">An array of paths to other resources required by the primary virtual resource.</param>
        /// <param name="utcStart">The UTC time at which the virtual resources were read.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Caching.CacheDependency"/> object for the specified virtual resources.
        /// </returns>
        public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
        {
            var baseDependencies = new List<string>();
            var templateDependencies = new List<string>();

            foreach (var dependency in virtualPathDependencies.Cast<string>())
            {
                if (base.FileExists(dependency) || base.DirectoryExists(dependency))
                {
                    baseDependencies.Add(dependency);
                }
                else
                {
                    templateDependencies.Add(dependency);
                }
            }

            var baseCacheDependency = base.GetCacheDependency(virtualPath, baseDependencies, utcStart);
            var templateCacheDependency = this.virtualTemplate.GetCacheDependency(virtualPath, templateDependencies, utcStart);

            if (baseCacheDependency == null && templateCacheDependency == null)
            {
                return null;
            }

            var cacheDependency = new AggregateCacheDependency();

            if (baseCacheDependency != null)
            {
                cacheDependency.Add(baseCacheDependency);
            }

            if (templateCacheDependency != null)
            {
                cacheDependency.Add(templateCacheDependency);
            }

            return cacheDependency;

        }

        /// <summary>
        /// Returns a hash of the specified virtual paths.
        /// </summary>
        /// <param name="virtualPath">The path to the primary virtual resource.</param>
        /// <param name="virtualPathDependencies">An array of paths to other virtual resources required by the primary virtual resource.</param>
        /// <returns>
        /// A hash of the specified virtual paths.
        /// </returns>
        public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
        {
            if (base.FileExists(virtualPath))
            {
                return base.GetFileHash(virtualPath, virtualPathDependencies);
            }

            return this.virtualTemplate.GetFileHash(virtualPath, virtualPathDependencies);
        }
    }
}
